package com.everwin.framework.aspectj;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.everwin.common.annotation.AddModifyId;
import com.everwin.common.enums.IdPrefix;
import com.everwin.common.json.JsonUtil;
import com.everwin.framework.util.WorkFlowSoftUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;

@Aspect
@Component
public class AddModifyIdAspect {

    private static final  String MODIFY_ID_PREFIX = "modified";
    private ObjectMapper objectMapper;

    @Autowired
    private JsonUtil jsonUtil;

    @Pointcut("@annotation(com.everwin.common.annotation.AddModifyId)")
    public void addModifyIdPointCut() {}

    @Around("addModifyIdPointCut()")
    public Object addModifyIdAround(ProceedingJoinPoint point) throws Throwable {
        Object proceed = point.proceed();
        if(objectMapper==null){
            objectMapper = new ObjectMapper();
            objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        }
        AddModifyId addModifyId = getAnnotationLog(point);
        if(addModifyId == null) {
            return proceed;
        }
        IdPrefix[] prefix = addModifyId.prefix();
        String[] pathArg = addModifyId.idPropertyNamePath();
        Class<?> aClass = addModifyId.resultType();
        JsonNode modifiedJson = getModifiedJson(proceed, prefix, pathArg);
        return objectMapper.treeToValue(modifiedJson, aClass);
    }

    private AddModifyId getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null) {
            return method.getAnnotation(AddModifyId.class);
        }
        return null;
    }

    private JsonNode getModifiedJson(Object proceed,IdPrefix[] prefix,String... pathArg) throws IOException {
        JsonNode jsonNode = jsonUtil.getJsonNodeByBean(proceed,objectMapper);
        for (int i = 0; i < pathArg.length; i++) {
            String[] onePathArg = pathArg[i].split("\\.");
            modifyNode(onePathArg,jsonNode,prefix[i].toString());
        }
        return jsonNode;
    }


    /**
     * 根据当前的id生成新的被修饰的id
     * 在id前面加上modified,生成驼峰式的id的key
     * @param idKey id的key
     * @return 被修饰id的key
     */
    private String createNewKey(String idKey){
        StringBuilder builder = new StringBuilder(MODIFY_ID_PREFIX);
        String firstChar = idKey.charAt(0)+"";
        builder.append(firstChar.toUpperCase());
        builder.append(idKey.substring(1));
        return builder.toString();
    }

    private void modifyNode(String[] everyPathArg,JsonNode jsonNode,String prefix){
        int argLen = everyPathArg.length;
        if(argLen == 1) {
            String idKey = everyPathArg[0];
            JsonNode idNode = jsonNode.path(idKey);
            long id = idNode.asLong();
            String modifyId = WorkFlowSoftUtil.createNumber(prefix,id);
            ((ObjectNode)jsonNode).put(createNewKey(idKey),modifyId);
            return;
        }
        for (int i = 0; i < everyPathArg.length-1; i++) {
            String firstPath = everyPathArg[i];
            JsonNode nextPathNode = jsonNode.path(firstPath);
            if(nextPathNode.isNull()){
                continue;
            }
            boolean isArray = nextPathNode.isArray();
            String[] sendArg = deduceFirst(everyPathArg);
            if(isArray){
                for(JsonNode node : nextPathNode){
                    modifyNode(sendArg,node,prefix);
                }
            }else{
                modifyNode(sendArg,nextPathNode,prefix);
            }
        }
    }

    private String[] deduceFirst(String[] arg){
        int argLen = arg.length;
        String[] newArg = new String[argLen-1];
        for (int i = 1; i < argLen; i++) {
            newArg[i-1] = arg[i];
        }
        return newArg;
    }
}
